package threadDemo.blockQueueDemo;

/*
阻塞队列的几个特点：
基本队列特性：先进先出。
写入队列空间不可用时会阻塞。
获取队列数据时当队列为空时将阻塞。

实现队列的方式多种，总的来说就是数组和链表；其实我们只需要搞清楚其中一个即可，不同的特性主要表现为数组和链表的区别

如何才能使一个线程被阻塞，看起来的表象线程卡住啥事也做不了。
等待通知机制
一旦队列满时就将写入线程调用 object.wait() 进入 waiting 状态，直到空间可用时再进行唤醒

写入队列满时会阻塞直到获取线程消费了队列数据后唤醒写入线程。
消费队列空时会阻塞直到写入线程写入了队列数据后唤醒消费线程。
*/
public final class ArrayQueue<T> {

    //    队列数量
    private int size = 0;

    //    队列中存储的内容
    private Object[] items;

    //    队列满时的阻塞 object.wait()
    private Object full = new Object();

    //    队列空时的阻塞 object.wait()
    private Object empty = new Object();

    //    写入数组时的下标
    private int putIndex;

    //    取出数组时的下标
    private int getIndex;

    public ArrayQueue(int size) {
        this.items = new Object[size];
    }

    //    放入队尾
    public void put(T t) {
        synchronized (full) {
            while (size == items.length) {
                try {
                    full.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        synchronized (empty) {
            //写入
            items[putIndex] = t;
            size++;
            putIndex++;

            if (putIndex == items.length) {
                //超过数组长度要从头开始
                putIndex = 0;
            }

            empty.notify();
        }
    }

    //    从队列头获取数据
    public T get() {
        synchronized (empty) {
            while (size == 0) {
                try {
                    empty.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

        synchronized (full) {
            Object res = items[getIndex];
            items[getIndex] = null;
            size--;

            getIndex++;
            if (getIndex == items.length) {
                getIndex = 0;
            }
            full.notify();
            return (T) res;
        }
    }

    public boolean isEmpty() {
        return size == 0;
    }
}
